-
Notifications
You must be signed in to change notification settings - Fork 6.1k
Introduce JKlib entry point #5558
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: 2.2.20
Are you sure you want to change the base?
Conversation
a4d6e96 to
2acd62f
Compare
| import org.jetbrains.kotlin.types.model.* | ||
| import org.jetbrains.kotlin.types.typeUtil.isUnit | ||
|
|
||
| /* |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The entire file is copy pasted from the corresponding Kotlin counterparts, except for the mangleTypeArgumentsUsingEffectiveVariance methods. This is needed to allow always using effective variance during mangling like mentioned in this comment .
| import org.jetbrains.kotlin.types.checker.SimpleClassicTypeSystemContext | ||
| import org.jetbrains.kotlin.types.typeUtil.replaceAnnotations | ||
|
|
||
| /* |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Most of the file is copied. The difference is in the signatureString methods that yield JVM signatures for Java methods.
| && descriptor !is PropertyGetterDescriptor | ||
| } | ||
|
|
||
| fun FunctionDescriptor.computeJvmDescriptor(withReturnType: Boolean = true, withName: Boolean = true): String = buildString { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function is mostly copied from org.jetbrains.kotlin.load.kotlin.MethodSignatureMappingKt#computeJvmDescriptor, except for the workaround described in the comment below.
|
|
||
| signature( | ||
| classDescriptor, | ||
| (original as? FunctionDescriptor ?: return null).computeJvmDescriptor() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the only different line from org.jetbrains.kotlin.load.kotlin.MethodSignatureMappingKt#computeJvmSignature. The original:
(original as? SimpleFunctionDescriptor ?: return null).computeJvmDescriptor()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just in case, the original function FunctionDescriptor.computeJvmDescriptor seems like some ad-hoc implementation that is only suitable for some specific purpose, and it would be risky to use it to compute JVM signatures of arbitrary declarations. For example, it doesn't support @JvmName, context parameters, constructors of inner/inline/sealed classes, and probably a lot more.
| return (JavaToKotlinClassMap.mapJavaToKotlin(this) ?: JavaToKotlinClassMap.mapKotlinToJava(toUnsafe())) != null | ||
| } | ||
|
|
||
| private fun withKotlinBuiltinsHack(idSig: IdSignature, f: () -> IrSymbol?): IrSymbol? { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the workaround for mapped types that I mentioned in the description of this PR
| mangler = mangler, | ||
| ) | ||
|
|
||
| val pluginContext = IrPluginContextImpl( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We are mocking an IrPluginContext here, because J2CL is currently operating with this type of context, but this can be changed in the future.
| ) | ||
|
|
||
| @Suppress("UNUSED") | ||
| fun compileKlibAndDeserializeIr( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the method that is called by J2CL to get the wanted IR.
| return md | ||
| } | ||
|
|
||
| private fun compileLibrary( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method performs the "first stage" of the compilation - compilation to klib
| } | ||
|
|
||
| @OptIn(InternalSymbolFinderAPI::class) | ||
| fun compileIr( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method performs the "second stage" - deserialze the klib and link IR.
|
Hey @nikita-nazarov ! Thanks a lot for sharing code and all relevant info. I'll be able to take a look at this in details next week. |
|
Hi @nikita-nazarov! As promised, here is the result of preliminary pre-review/internal discussions.
P.s. I'm going to be on vacation till December 9. With technical problems -- default gateway @kunyavskiy. With organizational questions, Pavel could also try to help. By default, I will return on December 9 and address all remaining organizational questions. |
Hi @erokhins, @kunyavskiy!
This PR is a preliminary version of the Kotlin compiler entry point for J2CL.
Currently there is a lot of mocking going around and there are a couple of workarounds introduced, so I believe some of the Kotlin API has to be modified to make this code cleaner. But anyway this PR should give you an impression of what we would like to achieve.
The ultimate goal would be to put this extension point in the Kotlin compiler repo together with some IR text tests, so that it would be possible to catch bugs early and to perform the required API migrations as fast as possible.
Here is a link to the related document.
Context
Currently, to transpile Kotlin code, J2CL acts as a compiler plugin. It is facing challenges in inlining Kotlin functions due to its reliance on the experimental and unsupported
-Xserialize-ircompiler flag to serialize the IR of a library in its corresponding header JAR file. This approach has led to issues such as orphaned IR nodes and incomplete IR serialization, particularly in complex scenarios like transitive inline calls. Additionally, compiling the standard library (stdlib) with this flag has presented further complications.These issues needed to be addressed before the wider adoption of J2CL’s Kotlin frontend. In that context, the Kotlin compiler team at JetBrains proposed the following solution that relies on klibs.The Klib pipeline
With this extension point J2CL transpilation happens roughly the following way:
Workarounds
When trying to implement this klib based pipeline we have encountered numerous issues (kudos to @kunyavskiy to help us fix them!), which made us introduce a couple of workarounds that are best described in this document:
For example given this code:
The imported
thenComparingfunction will have different signatures when serializing to klib with K2, and when deserializing with K1:To fix this issue, we put this signature to a klib:
We encountered issues when trying to resolve this declaration, because
kotlin.Throwabledoesn't definegetSuppressedin source code. This method comes from the correspondingjava.lang.Throwableclass. This made us introduce this workaround, which caches the IR symbols of Java declarations of mapped types.